package eval;

import network.Network;
import network.Rule;
import networkLoader.FBNetwork;
import partition.SubspacePartition;
import verifier.JiffyVerifier;
import verifier.Ports.PersistentPorts;
import verifier.util.Changes;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

public class ConcurrentEval1 {
    static Network network;

    public static void main(String[] args) {
        network = FBNetwork.getNetworkSrcHack();
        seq(false);
//        System.gc();
//        seq(true);
       SS(false);
//        System.gc();
//        SS(true);
//        SN(false);
//        System.gc();
//        SG(false);
//        System.gc();
//        SG(true);
//        System.gc();
        SSSG(false);
//        System.gc();
//        SSSG(true);
    }

    public static void seq(boolean useBatch) {
        System.out.println("useBatch: " + useBatch);
        JiffyVerifier verifier = new JiffyVerifier(network, new PersistentPorts());
        long s = System.nanoTime();
        if (useBatch) {
            s = System.nanoTime();
            Changes changes = verifier.insertMiniBatch(network.getInitialRules());
            verifier.update(changes);
        } else {
            for (Rule rule : network.getInitialRules()) {
                Changes changes = verifier.insertMiniBatch(new ArrayList<>(Collections.singletonList(rule)));
                verifier.update(changes);
            }
        }
        System.out.println("$seq: " + (System.nanoTime() - s));
        System.out.println("#EC: " + verifier.predSize());
    }

    public static void SN(boolean useBatch) {
        for (int i = 0; i < 112; i++) {
            List<Rule> rulesInSS = new ArrayList<>();
            for (Rule rule : network.getInitialRules()) {
                if (rule.getMatch().longValue() >> 24 == i) { // test pod1 as subnet
                    rulesInSS.add(rule);
                }
            }
            if (rulesInSS.size() == 0) break;

            JiffyVerifier verifier = new JiffyVerifier(network, new PersistentPorts());
            long s = System.nanoTime();
            if (useBatch) {
                s = System.nanoTime();
                Changes changes = verifier.insertMiniBatch(rulesInSS);
                verifier.update(changes);
            } else {
                for (Rule rule : rulesInSS) {
                    Changes changes = verifier.insertMiniBatch(new ArrayList<>(Collections.singletonList(rule)));
                    verifier.update(changes);
                }
            }
            long time = System.nanoTime() - s;
            System.out.println("====subspace " + i + "====");
            System.out.println("useBatch: " + useBatch);
            System.out.println("$SS: " + time);
            System.out.println("#rules: " + rulesInSS.size());
            System.out.println("#ECs: " + verifier.predSize());
        }

    }

    public static void SS(boolean useFFMT) {
        HashMap<Integer, List<Rule>> ssToRules = new HashMap<>();
        for (Rule rule : network.getInitialRules()) {
//            long rswId = (rule.getMatch().longValue() >> 8) & 0xFFFF;
//            int mciId = (int)((rswId - 1) / 48);
//            ssToRules.computeIfAbsent(mciId, k -> new ArrayList<>());
//            ssToRules.get(mciId).add(rule);
//            long rswId = (rule.getMatch().longValue() >> 8) & 0xFFFF;
//            int mciId = (int)((rswId - 1) / 48);
            long ssId = (rule.getMatch().longValue() >> 24);
            ssToRules.computeIfAbsent((int) (ssId), k -> new ArrayList<>());
            ssToRules.get((int) ssId).add(rule);
        }

        for (List<Rule> rules : ssToRules.values()) {
            SubspacePartition subspacePartition = new SubspacePartition(network, rules, false);
            subspacePartition.run(useFFMT);
        }
    }

    public static void SG(boolean useFFMT) {
        SubspacePartition subspacePartition = new SubspacePartition(network, network.getInitialRules(), true);
        subspacePartition.run(useFFMT);
    }

    public static void SSSG(boolean useFFMT) {
        HashMap<Integer, List<Rule>> ssToRules = new HashMap<>();
        for (Rule rule : network.getInitialRules()) {
            long ssId = (rule.getMatch().longValue() >> 24);
            ssToRules.computeIfAbsent((int) (ssId), k -> new ArrayList<>());
            ssToRules.get((int) ssId).add(rule);
        }


        for (List<Rule> rules : ssToRules.values()) {
//            BDDEngine bddEngine = new BDDEngine(32);
//            HashMap<String, SubgraphPartition> nameToSubgraphPartition = FattreeNetwork.getPartitions(network, 32, bddEngine);
            SubspacePartition subspacePartition = new SubspacePartition(network, true);
            subspacePartition.run(rules, useFFMT);
//            break;
        }
    }
}